/**
 * 
 */
package due.pc.pcshoot.gamelogic;

import java.io.IOException;

import org.anddev.andengine.audio.sound.Sound;
import org.anddev.andengine.audio.sound.SoundFactory;
import org.anddev.andengine.engine.Engine;
import org.anddev.andengine.engine.handler.physics.PhysicsHandler;
import org.anddev.andengine.entity.Entity;
import org.anddev.andengine.entity.IEntity;
import org.anddev.andengine.entity.modifier.EntityModifier;
import org.anddev.andengine.entity.modifier.FadeInModifier;
import org.anddev.andengine.entity.modifier.IEntityModifier;
import org.anddev.andengine.entity.modifier.MoveModifier;
import org.anddev.andengine.entity.modifier.ParallelEntityModifier;
import org.anddev.andengine.entity.modifier.SequenceEntityModifier;
import org.anddev.andengine.entity.scene.Scene;
import org.anddev.andengine.entity.scene.Scene.IOnSceneTouchListener;
import org.anddev.andengine.input.touch.TouchEvent;
import org.anddev.andengine.util.Debug;
import org.anddev.andengine.util.modifier.BaseModifier;
import org.anddev.andengine.util.modifier.IModifier;

import android.content.Context;
import android.util.Log;
import android.widget.Toast;

import due.pc.pcshoot.control.Objektiv;
import due.pc.pcshoot.control.OnScreenLogger;
import due.pc.pcshoot.control.Player;
import due.pc.pcshoot.entity.Ball;
import due.pc.pcshoot.entity.Canon;
import due.pc.pcshoot.entity.EnemyState;
import due.pc.pcshoot.entity.IEnemy;
import due.pc.pcshoot.entity.Ball.BallState;
import due.pc.pcshoot.entity.Ball.IBallStateChangeListener;

/**
 * @author manhhung.vu
 *
 */
public class BallLogicHandler implements IOnSceneTouchListener
{
	//===============================================================/
	//Constants
	//===============================================================/
	public static final long SHOOT_INTERVAL = 500;//Millisekunden
	public static final float SHOOT_FIRED_FADEIN_DURATION = 1f;
	public static final float SHOOT_FIRED_MOVE_DURATION = 2f;
	public static final float SHOOT_FIRED_FADEOUT_DURATION = 1f;
	
	public static final String SOUND_FIRED = "sound/fired.wav";
	public static final String SOUND_HIT = "sound/hit.wav";
	
	//===============================================================/
	//Fields
	//===============================================================/
	private Objektiv mObjektiv;
	private Canon mCanon;
	private Engine mEngine;
	private Context mContext;
	private Scene mScene;
	private IEnemy[] mEnemies;
	private Player mPlayer;
	private OnScreenLogger mOnScreenLogger;
	
	private Sound mFiredSound, mHitSound;
	
	private long mLastShootTime = System.currentTimeMillis();
	
	//===============================================================/
	//Constructors
	//===============================================================/
	public BallLogicHandler(
			Canon pCanon, Objektiv pObjektiv,
			Player pPlayer, OnScreenLogger pOnScreenLogger,
			IEnemy[] pEnemies, Scene pScene, 
			Engine pEngine, Context pContext) 
	{
		mObjektiv = pObjektiv;
		mCanon = pCanon;
		mEngine = pEngine;
		mContext = pContext;
		mScene = pScene;
		mEnemies = pEnemies;
		
		mPlayer = pPlayer;
		mOnScreenLogger = pOnScreenLogger;
		
		mScene.setOnSceneTouchListener(this);
		
		try 
		{
			mFiredSound = SoundFactory.createSoundFromAsset(mEngine.getSoundManager(), mContext, SOUND_FIRED);
			mHitSound = SoundFactory.createSoundFromAsset(mEngine.getSoundManager(), mContext, SOUND_HIT);
		} 
		catch (final IOException e) {
			Debug.e(e);
		}
	}
	
	//===============================================================/
	//Inherited Methods
	//===============================================================/
	@Override
	public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) 
	{
		long passedSec = System.currentTimeMillis() - mLastShootTime;
		
		if (passedSec >= SHOOT_INTERVAL)
		{
//			Toast.makeText(mContext, "Touch at " + pSceneTouchEvent.getX() + " " + 
//					pSceneTouchEvent.getY(), Toast.LENGTH_LONG);
			if (mCanon.getShotAmong() > 0)
			{
				Ball ball = Ball.createBall(0, 0, mEngine, mContext);
				float[] startPoint = getBallStartPoint(ball.getWidth(), ball.getHeight());
				float[] endPoint = getBallEndPoint(ball.getWidth(), ball.getHeight());
				
				ball.setPosition(startPoint[0], startPoint[1]);
				new FiredBallModifier(ball, endPoint[0], endPoint[1]);
				ball.setState(BallState.FIRED);
				
				mScene.attachChild(ball);
				mLastShootTime = System.currentTimeMillis();
				
				mCanon.decreaseShotAmong();
			}			
		}
		return false;
	}
	
	
	//===============================================================/
	//Methods
	//===============================================================/
	private float[] getBallStartPoint(float pBallWidth, float pBallHeight) 
	{
		float[] startPoint = mCanon.getFirePoint();
		startPoint[0] -= pBallWidth / 2;
		startPoint[1] -= pBallHeight / 2;
		
		Log.d("BallLogic", "startPoint " + startPoint[0] + " " + startPoint[1]);
		
		return startPoint;
	}
	
	private float[] getBallEndPoint(float pBallWidth, float pBallHeight) 
	{
		float[] endPoint = new float[] {mObjektiv.getX() + mObjektiv.getWidth() / 2, mObjektiv.getY() + mObjektiv.getHeight() / 2};
		endPoint[0] -= pBallWidth / 2;
		endPoint[1] -= pBallHeight / 2;
		
		Log.d("BallLogic", "endPoint " + endPoint[0] + " " + endPoint[1]);
		
		return endPoint;
	}
	
	
	//===============================================================/
	//Getters & Setters
	//===============================================================/
	
	
	//===============================================================/
	//Static Methods
	//===============================================================/
	
	
	//===============================================================/
	//Inner Classes
	//===============================================================/
	class BallDetachment implements Runnable
	{
		private Ball mBall;
		public BallDetachment(Ball pBall) {
			mBall = pBall;
		}
		
		@Override
		public void run() {
			mScene.detachChild(mBall);
		}
	}
	
	class FiredBallModifier extends EntityModifier implements IBallStateChangeListener
	{
		//===============================================================/
		//Constants
		//===============================================================/
		public static final float FIREDBALL_VELOCITY_FACTOR = 500;
		public static final float FIREDBALL_ANGULARVELOCITY_FACTOR = 135;
		
		
		//===============================================================/
		//Fields
		//===============================================================/
//		private PhysicsHandler mPhysicsHandler;
		private float mFromX, mFromY;
		private float mVelocityX, mVelocityY;
		private float mDistanceToDestination;
		
		//===============================================================/
		//Constructors
		//===============================================================/
		public FiredBallModifier(Ball pBall, float pMoveToX, float pMoveToY) 
		{
			mFromX = pBall.getX();
			mFromY = pBall.getY();
			
			float[] aVelocityXY = toVelocity(mFromX, mFromY, pMoveToX, pMoveToY);
			mDistanceToDestination = toDistance(mFromX, mFromY, pMoveToX, pMoveToY);
			
			mVelocityX = aVelocityXY[0];
			mVelocityY = aVelocityXY[1];
			
//			Log.d("FiredBallModifier", "Distance to Destination " + mDistanceToDestination);
			
			PhysicsHandler physicsHandler = new PhysicsHandler(pBall);
			physicsHandler.setVelocity(aVelocityXY[0], aVelocityXY[1]);//richtige konstante muss da sein
			physicsHandler.setAngularVelocity(FIREDBALL_ANGULARVELOCITY_FACTOR);
			pBall.registerUpdateHandler(physicsHandler);
			
			pBall.registerEntityModifier(this);
			pBall.setBallStateChangeListener(this);
		}

		//===============================================================/
		//Inherited Methods
		//===============================================================/
		@Override
		public EntityModifier clone()
				throws org.anddev.andengine.util.modifier.IModifier.CloneNotSupportedException {
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		public float getDuration() {
			// TODO Auto-generated method stub
			return 0;
		}

		@Override
		public float getSecondsElapsed() {
			// TODO Auto-generated method stub
			return 0;
		}

		@Override
		public float onUpdate(float pSecondsElapsed, IEntity pItem) 
		{
			Ball ball = (Ball) pItem;
			
			float goneDistance = toDistance(mFromX, mFromY, pItem.getX(), pItem.getY());
			if (goneDistance >= mDistanceToDestination) 
			{
				ball.setState(BallState.MISSED);
				removeBall(ball);
				return 0;
			}
			
			for (IEnemy enemy : mEnemies) {
				if (enemy.isVisible() && enemy.getState().equals(EnemyState.ACTIVE)) {			
					if (ball.collidesWith(enemy))
					{
						ball.setState(BallState.HIT);
						removeBall(ball);
						enemy.setState(EnemyState.HIT, mVelocityX, mVelocityY, ball.getX() + ball.getWidth() / 2, ball.getY() + ball.getHeight() / 2);
						
						mPlayer.addScores(enemy.getScore());
						mOnScreenLogger.setKilled(mOnScreenLogger.getKilled() + 1);
						
						return 0;
					}
				}
			}
			
			return 0;
		}

		@Override
		public void reset() {
			// TODO Auto-generated method stub
			
		}
		
		@Override
		public void onBallStateChanged(BallState pState, Ball pBall) 
		{
			if (pState.equals(BallState.HIT)) 
			{
				mHitSound.play();
			}
			else if (pState.equals(BallState.FIRED)) 
			{
				mFiredSound.play();
			}
			else if (pState.equals(BallState.MISSED))
			{
				
			}
		}
		
		//===============================================================/
		//Methods
		//===============================================================/
		private void removeBall(Ball pBall)
		{
			pBall.clearEntityModifiers();
			pBall.clearUpdateHandlers();
			mEngine.runOnUpdateThread(new BallDetachment(pBall));
		}
		
		private float[] toVelocity(float pCanonX, float pCanonY, float pMoveToX, float pMoveToY)
		{
			float deltaX = -(pCanonX - pMoveToX);
			float deltaY = -(pCanonY - pMoveToY);
			float distance = toDistance(pCanonX, pCanonY, pMoveToX, pMoveToY);
			
			float absDeltaX = Math.abs(deltaX);
			float absDeltaY = Math.abs(deltaY);
			
			float vx = (absDeltaX / distance);
			float vy = (absDeltaY / distance);
			
			vx *= FIREDBALL_VELOCITY_FACTOR * deltaX / absDeltaX;
			vy *= FIREDBALL_VELOCITY_FACTOR * deltaY / absDeltaY;
			
//			Log.d("Velocity", "x " + vx + " y " + vy);
			
			return new float[] {vx, vy};
		}
		
		private float toDistance(float pAx, float pAy, float pBx, float pBy)
		{
			float deltaX = pAx - pBx;
			float deltaY = pAy - pBy;
			
			return (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY);
		}
	}
}
